/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * @file           : main.c
 * @brief          : Main program body
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2024 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usb_device.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stmflash.h"
#include "usbd_custom_hid_if.h"

typedef void (*pFunction)(void);

unsigned long JumpAddress;
pFunction JumpToApplication;
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t senddata[64];
uint8_t st = 0;
uint32_t firmware_len = 0;
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

static void MX_GPIO_Init(void);

/* USER CODE BEGIN PFP */
void writeString(char *array, const char *data, int maxLength) {
  // 计算数据长度，包括结尾符 '\0'
  int dataLength = strlen(data);

  // 确保不会写入超过数组最大长度的数据
  int writeLength = (dataLength < maxLength) ? dataLength : maxLength - 1;

  // 复制数据到数组
  memcpy(array, data, writeLength);

  // 如果数据长度小于数组最大长度，则用0补齐
  if (dataLength < maxLength) {
    memset(array + writeLength, 0, maxLength - writeLength);
  }
}

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void System_jumpApp(uint32_t APP_ADDR) {
  if (((*(__IO uint32_t *)APP_ADDR) & 0x2FFE0000) == 0x20000000) {
    //		while (USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, senddata1,
    // 64)!=USBD_OK)	;
    USBD_Stop(&hUsbDeviceFS);
    USBD_DeInit(&hUsbDeviceFS);
    __set_PRIMASK(1); /*关闭全局中断，APP程序中带有RTOS时，这个很重要*/
    /* 关闭滴答时钟，复位*/
    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;
    HAL_SuspendTick(); // 挂起滴答定时器
    /* 设置所以时钟默认状态 */
    HAL_RCC_DeInit();

    /* 关闭所有中断，清除中断挂起标志 */
    for (uint8_t i = 0; i < 8; i++) {
      NVIC->ICER[i] = 0xFFFFFFFF;
      NVIC->ICPR[i] = 0xFFFFFFFF;
    }

    /* execute the new program */
    JumpAddress = *(__IO uint32_t *)(APP_ADDR + 4);

    __set_PSP(*(__IO uint32_t *)APP_ADDR);
    /* Jump to user application */
    JumpToApplication = (pFunction)JumpAddress;
    /* Initialize user application's Stack Pointer */
    __set_MSP(*(__IO uint32_t *)APP_ADDR); /* 设置主堆栈指针 */

    /* 在 RTOS 工程，这条语句很重要，设置为特权级模式，使用 MSP 指针 */
    __set_CONTROL(0);

    JumpToApplication();
  }
}

/* USER CODE END 0 */

/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void) {
  /* USER CODE BEGIN 1 */
  // 定义一个无符号32位整型变量t，并初始化为0
  uint32_t t = 0;
  // 定义一个无符号32位整型变量lentemp，并初始化为0
  uint32_t lentemp = 0;
  // 定义一个无符号16位整型数组writetemp，并初始化为32
  uint16_t writetemp[32];
  // 定义一个__IO无符号32位整型变量flashdestination，并初始化为APPLICATION_ADDRESS
  __IO uint32_t flashdestination = APPLICATION_ADDRESS;
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick.
   */
  HAL_Init();

  /* USER CODE BEGIN Init */
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1) {
    /* USER CODE END WHILE */
    switch (st) {
    case 0:
      // 如果usbread_state不为0，说明正在读取数据
      if (usbread_state) {
        // 如果读取到的数据中包含"Start upgrading"字符串
        if (strstr(usbdata, "Start upgrading") != NULL) {
          // 向数据发送缓冲区中写入" ok"字符串
          writeString(senddata, " ok", 64);
          // 向数据发送缓冲区发送数据
          while (USBD_CUSTOM_HID_SendReport_FS(senddata, 64) != USBD_OK)
            ;
          // 将状态设置为1
          st = 1;
          // 记录当前时间
          t = HAL_GetTick();
          // 将usbread_state设置为0
          usbread_state = 0;
        }
      } else {
        // 如果usbread_state为0，说明没有在读取数据
        // 如果当前时间与记录的时间差小于5000毫秒
        if (HAL_GetTick() - t < 5000) {
          // 向数据发送缓冲区中写入" Wait to upgrade"字符串
          writeString(senddata, " Wait to upgrade", 64);
          // 向数据发送缓冲区发送数据
          while (USBD_CUSTOM_HID_SendReport_FS(senddata, 64) != USBD_OK)
            ;
        } else {
          // 如果当前时间与记录的时间差大于等于5000毫秒，则跳转到指定地址
          System_jumpApp(APPLICATION_ADDRESS);
        }
      }
      break;
    case 1:
      // 如果usbread_state不为0，说明正在读取数据
      if (usbread_state) {
        // 读取固件长度
        firmware_len = (uint32_t)(usbdata[0] | (usbdata[1] << 8) |
                                  (usbdata[2] << 16) | (usbdata[3] << 24));
        // 向数据发送缓冲区中写入" ok"字符串
        writeString(senddata, " ok", 64);
        // 向数据发送缓冲区发送数据
        while (USBD_CUSTOM_HID_SendReport_FS(senddata, 64) != USBD_OK)
          ;
        // 延时1毫秒
        HAL_Delay(1);
        // 将状态设置为2
        st = 2;
        // 将usbread_state设置为0
        usbread_state = 0;
      }
      break;
    case 2:
      // 如果usbread_state不为0，说明正在读取数据
      if (usbread_state) {
        // 如果固件长度大于已读取到的数据长度
        if (firmware_len > lentemp) {
          // 读取数据
          for (int i = 0; i < 63; i += 2) {
            writetemp[i / 2] = (uint16_t)(usbdata[i] | (usbdata[i + 1] << 8));
          }
          // 将数据写入指定地址
          stmflash_write(flashdestination + lentemp * 64, writetemp, 32);
          // 向数据发送缓冲区中写入" ok"字符串
          writeString(senddata, " ok", 64);
          // 向数据发送缓冲区发送数据
          while (USBD_CUSTOM_HID_SendReport_FS(senddata, 64) != USBD_OK)
            ;
          // 记录已读取到的数据长度
          lentemp++;
          // 如果固件长度等于已读取到的数据长度，则跳转到指定地址
          if (firmware_len == lentemp) {
            System_jumpApp(APPLICATION_ADDRESS);
          }
        }
        // 将usbread_state设置为0
        usbread_state = 0;
      }

      break;
    }
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void) {
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
   * in the RCC_OscInitTypeDef structure.
   */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
   */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |
                                RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
  PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
    Error_Handler();
  }
}

/**
 * @brief GPIO Initialization Function
 * @param None
 * @retval None
 */
static void MX_GPIO_Init(void) {
  /* USER CODE BEGIN MX_GPIO_Init_1 */
  /* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /* USER CODE BEGIN MX_GPIO_Init_2 */
  /* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
 * @brief  This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void) {
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1) {
    ;
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
 * @brief  Reports the name of the source file and the source line number
 *         where the assert_param error has occurred.
 * @param  file: pointer to the source file name
 * @param  line: assert_param error line source number
 * @retval None
 */
void assert_failed(uint8_t *file, uint32_t line) {
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line
     number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file,
     line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
